package models

import (
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"video/config"
	"video/global"
)

var pathList = make(chan *global.FileInfo)
var SaveFilePath = config.Get("SAVE_FILE_PATH")
var ImageNums = config.GetInt("IMAGE_CHAN_NUM")
var SaveImagesPath = config.Get("SAVE_IMAGES_PATH")
var FILM_EXTENSIONS = config.GetStringSlice("FILM_EXTENSIONS")
var FileExtensions = []string{}

// 判断字符串是否在切片中
func containsString(slice []string, str string) bool {
	for _, item := range slice {
		if item == str {
			return true
		}
	}
	return false
}

func isExist(info *global.FileInfo) bool {
	for _, item := range global.ShowList {
		if item.Name == info.Name && item.Path == info.Path {
			return true
		}
	}
	return false
}

// saveImageMapping 将视频和对应的图片映射关系保存到文件
func saveFileInfo(info *global.FileInfo) error {
	// 将结构体编码为JSON格式的字节切片
	jsonData, err := json.Marshal(info)
	if err != nil {
		fmt.Println("写入文件失败：", info)
		return err
	}

	// 打开文件用于追加，如果文件不存在则创建它
	file, err := os.OpenFile(SaveFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Printf("打开文件时发生错误: %v", err)
	}
	defer file.Close() // 确保关闭文件
	// 将字节切片写入文件末尾
	if _, err := file.WriteString(string(jsonData) + "\n"); err != nil {
		fmt.Printf("追加到文件时发生错误: %v", err)
	}
	return nil
}

// getVideoDuration 使用ffmpeg获取视频时长（秒）
func getVideoDuration(videoPath string) (float64, error) {
	cmd := exec.Command("ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", videoPath)
	output, err := cmd.Output()
	if err != nil {
		return 0, err
	}
	duration, err := strconv.ParseFloat(strings.TrimSpace(string(output)), 64)
	if err != nil {
		return 0, err
	}
	return duration, nil
}

// extractFrame 使用ffmpeg从视频中提取指定时间点的帧
func extractFrame(videoPath string, timestamp float64, outputPath string) error {
	cmd := exec.Command("ffmpeg", "-ss", fmt.Sprintf("%.2f", timestamp), "-i", videoPath, "-vframes", "1", "-q:v", "2", outputPath)
	err := cmd.Run()
	if err != nil {
		return err
	}
	return nil
}

// processVideo 处理单个视频文件
func processVideo(videoPath string) ([]string, error) {
	var images []string
	//videoDir := filepath.Dir(videoPath)
	videoName := filepath.Base(videoPath)
	videoNameWithoutExt := strings.TrimSuffix(videoName, filepath.Ext(videoName))

	duration, err := getVideoDuration(videoPath)
	if err != nil {
		return images, err
	}

	// 截取视频时长的1/3, 2/3时间点
	var timePoints []float64
	for i := 1; i <= ImageNums; i++ {
		timePoints = append(timePoints, float64(i)/float64(ImageNums+1))
	}
	for _, point := range timePoints {
		frameTime := duration * point
		frameName := fmt.Sprintf("%s_%.2f.jpg", videoNameWithoutExt, frameTime)
		framePath := filepath.Join(SaveImagesPath, frameName)

		err = extractFrame(videoPath, frameTime, framePath)
		if err != nil {
			fmt.Printf("提取视频帧时出错 路径： %s 时间点 %.2f: %v\n", videoPath, frameTime, err)
			continue
		}
		images = append(images, framePath)
	}

	return images, nil
}

// WalkVideos walkVideos 遍历目录并处理每个视频文件
func WalkVideos(dirPath string) error {
	go work()
	err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			fmt.Printf("访问路径时出错 :%q: %v\n", path, err)
			return err
		}
		if !info.IsDir() {
			ext := filepath.Ext(path)
			if !containsString(FileExtensions, ext) { // 你可以根据需要添加更多的视频格式
				FileExtensions = append(FileExtensions, ext)
				fmt.Println(FileExtensions)
			}
			if containsString(FILM_EXTENSIONS, ext) { // 你可以根据需要添加更多的视频格式
				file := &global.FileInfo{Path: path, Name: info.Name()}
				if !isExist(file) {
					pathList <- file
				} else {
					fmt.Printf("视频已存在 %s: %v\n", info.Name(), err)
				}
			} else {
				fmt.Printf("不是视屏格式: %s: %s\n", info.Name(), ext)
			}
		}
		return nil
	})
	defer close(pathList)
	return err
}
func work() {
	for i := 1; i <= ImageNums; i++ {
		go func() {
			for {
				file, ok := <-pathList
				if ok {
					lists, err := processVideo(file.Path)
					if err != nil {
					} else {
						file.Images = lists
						saveFileInfo(file)
						fmt.Printf("完成： %s \n", file.Name)
					}
				} else {
					fmt.Printf("通道关闭")
					break
				}
			}
		}()
	}
}
